package com.marco.dao.common;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.marco.dao.common.query.Filter;
import com.marco.dao.common.query.Order;
import com.marco.dao.common.query.QueryBuilder;
import com.marco.dao.common.wrapper.IEntityWrapper;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.core.ResolvableType;

import java.util.Arrays;
import java.util.Collection;
import java.util.Date;

/**
 * @Author zixiaojun
 * @Description
 * @Date 2021/3/3 4:07 下午
 */
abstract class BaseDaoSupport<M extends BaseMapper<T>, T> extends ServiceImpl<M, T> {

    /**
     * 实体类类型
     */
    private Class<T> entityClass;

    public BaseDaoSupport() {
        ResolvableType resolvableType = ResolvableType.forClass(getClass());
        entityClass = (Class<T>) resolvableType.getSuperType().getGeneric(1).resolve();
    }

    public IEntityWrapper<T> getWrapper(QueryBuilder<T> queryBuilder) {
        IEntityWrapper<T> entityWrapper = new IEntityWrapper<>();
        if (queryBuilder instanceof QueryBuilder && queryBuilder.getEntity() != null) {
            entityWrapper.setEntity(queryBuilder.getEntity());
        }
        if (queryBuilder == null) {
            return entityWrapper;
        }
        if (!CollectionUtils.isEmpty(queryBuilder.getFilters())) {
            queryBuilder.getFilters().forEach(filter -> {
                if (filter != null) {
                    toPredicate(entityWrapper, filter);
                }
            });
        }
        if (!CollectionUtils.isEmpty(queryBuilder.getOrders())) {
            queryBuilder.getOrders().forEach(order -> {
                if (order != null) {
                    toOrder(entityWrapper, order);
                }
            });
        }
        return entityWrapper;
    }

    private void toPredicate(IEntityWrapper<T> wrapper, Filter filter) {
        String property = filter.getProperty();
        Filter.Operator operator = filter.getOperator();
        Object value = filter.getValue();
        Boolean ignoreCase = filter.getIgnoreCase();
        switch (operator) {
            case eq:
                if (value != null) {
                    if (BooleanUtils.isTrue(ignoreCase) && value instanceof String) {
                        wrapper.apply(String.format("LOWER(%s) = {0}", property), ((String) value).toLowerCase());
                    } else {
                        wrapper.eq(property, value);
                    }
                }
                break;
            case ne:
                if (value != null) {
                    if (BooleanUtils.isTrue(ignoreCase) && value instanceof String) {
                        wrapper.apply(String.format("LOWER(%s) <> {0}", property), ((String) value).toLowerCase());
                    } else {
                        wrapper.ne(property, value);
                    }
                }
                break;
            case gt:
                wrapper.gt(property, value);
                break;
            case lt:
                wrapper.lt(property, value);
                break;
            case ge:
                wrapper.ge(property, value);
                break;
            case le:
                wrapper.le(property, value);
                break;
            case like:
                if (BooleanUtils.isTrue(ignoreCase)) {
                    wrapper.apply(String.format("LOWER(%s) like {0}", property), "%"+String.valueOf(value).toLowerCase()+"%");
                } else {
                    wrapper.like(property, String.valueOf(value));
                }
                break;
            case notLike:
                if (BooleanUtils.isTrue(ignoreCase)) {
                    wrapper.apply(String.format("LOWER(%s) not like {0}", property), "%"+String.valueOf(value).toLowerCase()+"%");
                } else {
                    wrapper.notLike(property, String.valueOf(value));
                }
                break;
            case in:
                if (value instanceof Collection) {
                    wrapper.in(property, (Collection<?>) value);
                } else if (value instanceof Object[]) {
                    wrapper.in(property, Arrays.asList((Object[]) value));
                } else if (value instanceof String) {
                    wrapper.in(property, (String) value);
                }
                break;
            case notIn:
                if (value instanceof Collection) {
                    wrapper.notIn(property, (Collection<?>) value);
                } else if (value instanceof Object[]) {
                    wrapper.notIn(property, Arrays.asList(value));
                } else if (value instanceof String) {
                    wrapper.notIn(property, (String) value);
                }
                break;
            case isNull:
                wrapper.isNull(property);
                break;
            case isNotNull:
                wrapper.isNotNull(property);
                break;
            case between:
                if (value != null && value instanceof Object[]) {
                    Object[] values = (Object[]) value;
                    if (values.length == 2) {
                        wrapper.between(property, values[0], values[1]);
                    }
                }
                break;
            case groupBy:
                wrapper.groupBy(property);
                break;

            case distinct:
                wrapper.select(String.format("DISTINCT %s",property));
                break;

            case or:
                wrapper.or();
                break;
            case limit:
                if (value != null) {
                    if (value instanceof Integer[]) {
                        Integer[] values = (Integer[]) value;
                        if (values.length == 2) {
                            wrapper.limit(values[0], values[1]);
                        }
                    } else if (value instanceof Integer) {
                        wrapper.limit((Integer) value);
                    }
                }
                break;
            case eqFmtDate:
                if (value != null && value instanceof Object[]) {
                    Object[] values = (Object[]) value;
                    if (values.length == 2) {
                        if (values[0] instanceof Date && values[1] instanceof String) {
                            wrapper.apply(String.format("DATE_FORMAT(%s,'%s') = DATE_FORMAT({0},'%s')", property, values[1],values[1]), DateFormatUtils.format((Date)values[0],"yyyy-MM-dd HH:mm:ss"));
                        }
                    }
                }
                break;
        }
    }

    public void toOrder(IEntityWrapper<T> wrapper, Order order) {
        String property = order.getProperty();
        Order.Direction direction = order.getDirection();
        switch (direction) {
            case asc:
                wrapper.orderByAsc(property);
                break;
            case desc:
                wrapper.orderByDesc(property);
                break;
        }
    }

}
